﻿using EnvDTE;
using EnvDTE80;
using Microsoft.VisualStudio.Shell;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
using jTCodeGenerate.Models;

namespace jTCodeGenerate
{
    public class MainCommandBase
    {
        private MainCommandBase() { }
        private static MainCommandBase _instance;
        private static readonly object _lockObj = new object();
        private DTE2 _dte;
        private IList<Project> _solutionProjects;
        private string _ignoreSuffix = Command.SQLCommand.GetSettingValue("IgnoreSuffix");

        public DTE2 Dte
        {
            get
            {
                return _dte;
            }
        }
        public IList<Project> SolutionProjects
        {
            get
            {
                return _solutionProjects;
            }
        }
        public void Init(DTE2 dte, IList<Project> solutionProjects)
        {
            _dte = dte;
            _solutionProjects = solutionProjects;
        }
        public static MainCommandBase Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (_lockObj)
                    {
                        if (_instance == null)
                        {
                            _instance = new MainCommandBase();
                        }
                    }
                }
                return _instance;
            }
        }
        #region 辅助方法
        /// <summary>
        /// 获取所有项目
        /// </summary>
        /// <param name="projectItems"></param>
        /// <returns></returns>
        public IEnumerable<ProjectItem> GetProjects(ProjectItems projectItems)
        {
            Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread();
            foreach (EnvDTE.ProjectItem item in projectItems)
            {
                yield return item;

                if (item.SubProject != null)
                {
                    foreach (EnvDTE.ProjectItem childItem in GetProjects(item.SubProject.ProjectItems))
                        if (childItem.Kind == EnvDTE.Constants.vsProjectItemKindSolutionItems)
                            yield return childItem;
                }

                if (item.ProjectItems != null)
                {
                    foreach (EnvDTE.ProjectItem childItem in GetProjects(item.ProjectItems))
                        if (childItem.Kind == EnvDTE.Constants.vsProjectItemKindSolutionItems)
                            yield return childItem;
                }
            }
        }

        /// <summary>
        /// 获取解决方案里面所有项目
        /// </summary>
        /// <param name="solution"></param>
        /// <returns></returns>
        public List<Project> GetSolutionProjects(Solution solution)
        {
            Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread();
            var projects = solution.Projects.OfType<Project>().ToList();
            return projects;
            //foreach (var project in projects)
            //{
            //    var applicationNewFolder = project.ProjectItems.Item("Dto");
            //    if (applicationNewFolder == null)
            //    {
            //        applicationNewFolder = project.ProjectItems.AddFolder("Dto");
            //    }
            //}
        }

        /// <summary>
        /// 获取类
        /// </summary>
        /// <param name="codeElements"></param>
        /// <returns></returns>
        public CodeClass GetClass(CodeElements codeElements)
        {
            Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread();
            var elements = codeElements.Cast<CodeElement>().ToList();
            CodeClass result = null;
            foreach (var codeElement in elements)
            {
                if (codeElement.Kind == vsCMElement.vsCMElementClass)
                {
                    result = codeElement as CodeClass;
                    break;
                }
            }

            if (result != null)
            {
                return result;
            }
            foreach (var codeElement in elements)
            {
                result = GetClass(codeElement.Children);
                if (result != null)
                {
                    return result;
                }
            }
            return null;
        }

        /// <summary>
        /// 获取类对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="codeElements"></param>
        /// <param name="vsCMElement"></param>
        /// <param name="codeClasses"></param>
        public void GetClassList(CodeElements codeElements, ref List<CodeClass> codeClasses)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var elements = codeElements.Cast<CodeElement>().ToList();
            CodeClass result = null;
            foreach (var codeElement in elements)
            {
                if (codeElement.Kind == vsCMElement.vsCMElementClass)
                {
                    result = codeElement as CodeClass;
                    break;
                }
            }
            if (result != null)
            {
                codeClasses.Add(result);
            }
            foreach (var codeElement in elements)
            {
                if (codeElement.Kind == vsCMElement.vsCMElementOther || codeElement.Kind == vsCMElement.vsCMElementClass)
                    continue;

                GetClassList(codeElement.Children, ref codeClasses);
            }
        }

        /// <summary>
        /// 获取接口对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="codeElements"></param>
        /// <param name="vsCMElement"></param>
        /// <param name="codeInterfaces"></param>
        public void GetInterfaceList(CodeElements codeElements, ref List<CodeInterface> codeInterfaces)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var elements = codeElements.Cast<CodeElement>().ToList();
            CodeInterface result = null;
            foreach (var codeElement in elements)
            {
                if (codeElement.Kind == vsCMElement.vsCMElementInterface)
                {
                    result = codeElement as CodeInterface;
                    break;
                }
            }
            if (result != null)
            {
                codeInterfaces.Add(result);
            }
            foreach (var codeElement in elements)
            {
                if (codeElement.Kind == vsCMElement.vsCMElementOther || codeElement.Kind == vsCMElement.vsCMElementClass)
                    continue;
                GetInterfaceList(codeElement.Children, ref codeInterfaces);
            }
        }

        /// <summary>
        /// 获取所有类
        /// </summary>
        /// <param name="projectItems"></param>
        /// <param name="codeClasses"></param>
        public void GetProjectClassAll(ProjectItems projectItems, ref List<CodeClass> codeClasses)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            foreach (var item in projectItems)
            {
                var cc = item as ProjectItem;
                if (cc.FileCodeModel != null && cc.FileCodeModel.CodeElements != null)
                    GetClassList(cc.FileCodeModel.CodeElements, ref codeClasses);
                else
                    GetProjectClassAll(cc.ProjectItems, ref codeClasses);
            }
        }

        /// <summary>
        /// 获取当前所选文件去除项目目录后的文件夹结构
        /// </summary>
        /// <param name="selectProjectItem"></param>
        /// <returns></returns>
        public string GetSelectFileDirPath(Project topProject, ProjectItem selectProjectItem)
        {
            Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread();
            string dirPath = "";
            if (selectProjectItem != null)
            {
                //所选文件对应的路径
                string fileNames = selectProjectItem.FileNames[0];
                string selectedFullName = fileNames.Substring(0, fileNames.LastIndexOf('\\'));

                //所选文件所在的项目
                if (topProject != null)
                {
                    //项目目录
                    string projectFullName = topProject.FullName.Substring(0, topProject.FullName.LastIndexOf('\\'));

                    //当前所选文件去除项目目录后的文件夹结构
                    dirPath = selectedFullName.Replace(projectFullName, "");
                }
            }

            return dirPath.Substring(1);
        }

        /// <summary>
        /// 添加文件到项目中
        /// </summary>
        /// <param name="folder"></param>
        /// <param name="content"></param>
        /// <param name="fileName"></param>
        public void AddFileToProjectItem(ProjectItem folder, IEnumerable<string> content, string fileName)
        {
            Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread();
            AddFileToProjectItem(folder.ProjectItems, content, fileName);
        }

        public void AddFileToProjectItem(ProjectItems projectItems, IEnumerable<string> content, string fileName)
        {
            Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread();
            try
            {
                string path = Path.GetTempPath();
                Directory.CreateDirectory(path);
                string file = Path.Combine(path, fileName);
                File.WriteAllLines(file, content, System.Text.Encoding.UTF8);
                try
                {
                    projectItems.AddFromFileCopy(file);
                }
                finally
                {
                    File.Delete(file);
                }
            }
            catch (Exception ex)
            {

            }
        }

        /// <summary>
        /// 添加文件到指定目录
        /// </summary>
        /// <param name="directoryPathOrFullPath"></param>
        /// <param name="content"></param>
        /// <param name="fileName"></param>
        public void AddFileToDirectory(string directoryPathOrFullPath, string content, string fileName = "")
        {
            try
            {
                string file = string.IsNullOrEmpty(fileName) ? directoryPathOrFullPath : Path.Combine(directoryPathOrFullPath, fileName);
                File.WriteAllText(file, content, System.Text.Encoding.UTF8);
            }
            catch (Exception ex)
            {

            }
        }
        #endregion
        private string GetAttributeValue(string val, string name)
        {
            var description = string.Empty;
            if (!string.IsNullOrEmpty(val))
            {
                string displayStr = val.Trim();
                foreach (var displayValueStr in displayStr.Split(','))
                {
                    if (!string.IsNullOrEmpty(displayValueStr))
                    {
                        if (displayValueStr.Split('=')[0].Trim() == name)
                        {
                            description = displayValueStr.Split('=')[1].Trim().Replace("\"", "");
                        }
                    }
                }
            }
            return description;
        }
        public string GetDescription(CodeElements attributes)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            return GetAttrbuteValue(attributes, "Description", "");
        }

        public string GetColumnName(CodeElements attributes)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            return GetAttrbuteValue(attributes, "Column", "");
        }

        public string GetTableName(CodeElements attributes)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            return GetAttrbuteValue(attributes, "Table", "");
        }

        public string GetMaxLength(CodeElements attributes)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            return GetAttrbuteValue(attributes, "MaxLength", "");
        }

        private string GetAttrbuteValue(CodeElements attributes, string attributeName, string attributePropertyName)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var description = string.Empty;
            foreach (CodeAttribute classAttribute in attributes)
            {
                if (classAttribute.Name == attributeName)
                {
                    if (string.IsNullOrWhiteSpace(attributePropertyName))
                        description = classAttribute.Value.Replace("\"", "");
                    else
                        description = GetAttributeValue(classAttribute.Value, attributePropertyName);
                }
            }
            return description;
        }

        /// <summary>
        /// 获取类字段
        /// </summary>
        /// <param name="classCode"></param>
        /// <returns></returns>
        public List<ClassProperty> GetClassProperty(CodeClass classCode)
        {
            Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread();
            var result = new List<ClassProperty>();
            foreach (CodeElement codeMember in classCode.Members)
            {
                var property = codeMember as CodeProperty;
                if (property != null)
                {
                    var propertyName = property.Name;
                    var propertyType = string.Empty;
                    switch (property.Type.TypeKind)
                    {
                        case vsCMTypeRef.vsCMTypeRefString:
                            propertyType = "string";
                            break;

                        case vsCMTypeRef.vsCMTypeRefInt:
                            propertyType = "int";
                            break;

                        case vsCMTypeRef.vsCMTypeRefLong:
                            propertyType = "long";
                            break;

                        case vsCMTypeRef.vsCMTypeRefByte:
                            propertyType = "byte";
                            break;

                        case vsCMTypeRef.vsCMTypeRefChar:
                            propertyType = "char";
                            break;

                        case vsCMTypeRef.vsCMTypeRefShort:
                            propertyType = "short";
                            break;

                        case vsCMTypeRef.vsCMTypeRefBool:
                            propertyType = "bool";
                            break;

                        case vsCMTypeRef.vsCMTypeRefDecimal:
                            propertyType = "decimal";
                            break;

                        case vsCMTypeRef.vsCMTypeRefFloat:
                            propertyType = "float";
                            break;

                        case vsCMTypeRef.vsCMTypeRefDouble:
                            propertyType = "double";
                            break;
                        default:
                            propertyType = property.Type.AsFullName;
                            break;
                    }
                    var columnName = GetColumnName(property.Attributes);
                    var propertyDescription = GetDescription(property.Attributes);
                    var maxLength = GetMaxLength(property.Attributes);
                    if (string.IsNullOrWhiteSpace(columnName))
                        columnName = propertyName;
                    result.Add(new ClassProperty { PropertyName = propertyName, PropertyType = propertyType, Describe = propertyDescription, ColumnName = columnName, MaxLength = maxLength });
                }
            }
            return result;
        }

        /// <summary>
        /// 获取当前选中的项
        /// </summary>
        public ProjectItem GetCurrentProjectItem(DTE2 dte)
        {
            Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread();
            var selectedItem = dte.SelectedItems.Item(1);
            ProjectItem selectProjectItem = selectedItem.ProjectItem;
            return selectProjectItem;
        }

        /// <summary>
        /// 获取当前选中的项目
        /// </summary>
        /// <param name="dte"></param>
        /// <returns></returns>
        public Project GetCurrentProject(DTE2 dte)
        {
            Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread();
            var selectedItem = dte.SelectedItems.Item(1);
            return selectedItem.Project;
        }

        /// <summary>
        /// 获取当前项目路径
        /// </summary>
        /// <param name="dte"></param>
        public string GetProjectPath(DTE2 dte)
        {
            Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread();
            return GetCurrentProjectItem(dte).ContainingProject.FullName;
        }

        /// <summary>
        /// 获取当前类
        /// </summary>
        /// <param name="dte"></param>
        /// <returns></returns>
        public List<CodeClass> GetCurrentClass(DTE2 dte)
        {
            Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread();
            var codeClassList = new List<CodeClass>();
            var selectProjectItem = GetCurrentProjectItem(dte);
            if (selectProjectItem == null)
            {
                var selectedItem = dte.SelectedItems.Item(1);
                var projectItems = selectedItem.Project.ProjectItems;
                GetProjectClassAll(projectItems, ref codeClassList);
                return codeClassList;
            }
            if (selectProjectItem.FileCodeModel == null)
            {
                var projectItems = selectProjectItem.ProjectItems;
                GetProjectClassAll(projectItems, ref codeClassList);
                return codeClassList;
            }

            var cla = GetClass(selectProjectItem.FileCodeModel.CodeElements);
            codeClassList.Add(cla);
            return codeClassList;
        }

        /// <summary>
        /// 获取当前项目命名空间
        /// </summary>
        /// <param name="dte"></param>
        /// <returns></returns>
        public string GetCurrentProjectNamespace(DTE2 dte)
        {
            Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread();
            var selectProjectItem = GetCurrentProjectItem(dte);
            return selectProjectItem.FileCodeModel.CodeElements.OfType<CodeNamespace>().First().FullName;
        }

        /// <summary>
        /// 深拷贝
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <returns></returns>
        public T DeepCopy<T>(T obj)
        {
            object retval;
            using (MemoryStream ms = new MemoryStream())
            {
                XmlSerializer xml = new XmlSerializer(typeof(T));
                xml.Serialize(ms, obj);
                ms.Seek(0, SeekOrigin.Begin);
                retval = xml.Deserialize(ms);
                ms.Close();
            }
            return (T)retval;
        }

        public void MySqlSetField(string fieldType, string maxLength, ref TableFieldModel tableFieldModel)
        {
            var str = string.Empty;
            if (fieldType.Contains("Nullable"))
            {
                tableFieldModel.IsNull = true;
                var bidx = fieldType.IndexOf('<') + 1;
                str = fieldType.Substring(bidx);
                var eidx = str.LastIndexOf('>');
                str = str.Substring(0, eidx);
            }
            if (string.IsNullOrWhiteSpace(str))
                str = fieldType;
            switch (str)
            {
                case "string":
                    if (!string.IsNullOrWhiteSpace(maxLength))
                    {
                        int.TryParse(maxLength, out int result);
                        if (result > 0)
                        {
                            tableFieldModel.TypeNameFull = $"varchar({result})";
                            tableFieldModel.TypeBeginLength = result;
                        }
                    }
                    tableFieldModel.TypeName = "logntext";
                    break;
                case "System.Int32":
                case "int":
                    tableFieldModel.TypeNameFull = "int(11)";
                    tableFieldModel.TypeBeginLength = 11;
                    break;
                case "System.Int64":
                case "long":
                    tableFieldModel.TypeNameFull = "bigint(20)";
                    tableFieldModel.TypeBeginLength = 20;
                    break;
                case "System.Single":
                case "float":
                    tableFieldModel.TypeNameFull = "float";
                    break;
                case "System.Double":
                case "double":
                    tableFieldModel.TypeNameFull = "double";
                    break;
                case "datetime":
                    tableFieldModel.TypeNameFull = "datetime";
                    break;
                case "System.Byte[]":
                case "byte[]":
                    tableFieldModel.TypeNameFull = "longblob";
                    break;
                case "System.Decimal":
                case "decimal":
                    tableFieldModel.TypeNameFull = "decimal(65,30)";
                    tableFieldModel.TypeBeginLength = 65;
                    tableFieldModel.TypeEndLength = 30;
                    break;
                case "guid":
                    tableFieldModel.TypeNameFull = "char(36)";
                    tableFieldModel.TypeBeginLength = 36;
                    break;
                case "short":
                    tableFieldModel.TypeNameFull = "smallint(6)";
                    tableFieldModel.TypeBeginLength = 6;
                    break;
                case "bool":
                    tableFieldModel.TypeNameFull = "tinyint(1)";
                    tableFieldModel.TypeBeginLength = 1;
                    break;
                case "System.DateTime":
                    tableFieldModel.TypeNameFull = "datetime(6)";
                    tableFieldModel.TypeBeginLength = 6;
                    break;
                default:
                    break;
            }
        }

        public List<ProjectModel> InitProjectFolderItem()
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var projectModels = new List<ProjectModel>();
            foreach (var item in SolutionProjects)
            {
                var lst = new List<ProjectItemModel>();
                GetProjectFolderItems(item.ProjectItems, lst);
                projectModels.Add(new ProjectModel { Name = item.Name, Project = item, ProjectItemModels = lst });
            }
            return projectModels;
        }
        
        private void GetProjectFolderItems(ProjectItems projectItems, IList<ProjectItemModel> projectItemsModels)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            if (projectItems == null || projectItems.Count <= 0)
                return;
            var ignoreSuffixArr = _ignoreSuffix.Split(',');
            for (int i = 1; i <= projectItems.Count; i++)
            {
                var item = projectItems.Item(i);
                if (item != null)
                {
                    var ext = Path.GetExtension(item.Name);
                    if (ignoreSuffixArr.Length > 0 && ignoreSuffixArr.Contains(ext))
                        continue;
                    var en = new ProjectItemModel { Name = item.Name, Item = item };
                    projectItemsModels.Add(en);
                    if ((item.ProjectItems != null && item.ProjectItems.Count > 0))
                    {
                        if (en.Childs == null)
                            en.Childs = new List<ProjectItemModel>();
                        GetProjectFolderItems(item.ProjectItems, en.Childs);
                    }

                    if ((item.SubProject != null && item.SubProject.ProjectItems != null && item.SubProject.ProjectItems.Count > 0))
                    {
                        if (en.Childs == null)
                            en.Childs = new List<ProjectItemModel>();
                        GetProjectFolderItems(item.SubProject.ProjectItems, en.Childs);
                    }
                }
            }
        }
    }
}
